home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
mail
/
delivery
/
smail-3.002
/
smail-3
Wrap
Text File
|
1994-12-13
|
53KB
|
1,686 lines
SMTP Extensions for Smail 3.1.29.1
Version 2
by Simon Leinen <simon@lia.di.epfl.ch>
DESCRIPTION
-----------
This file contains a patch to the Smail 3.1.29.1 sources that
implements RFCs 1425, 1426 and 1427 as well as the Internet Draft
`draft-ietf-smtpext-pipeline-02.txt'. The following features are
implemented:
* The new EHLO command is recognized as mandated by [RFC1425]. The
list of supported extensions in the reply includes HELP, EXPN and
optionally 8BITMIME and SIZE. If a message is received from a
remote mailer that said EHLO, the Received: line that is added to
the message will contain a sender protocol of "esmtp" rather than
"stmp" (or "ebsmtp" rather than "bsmtp").
* The PIPELINING extension is provided by the server (this did not
necessiatate any modifications of how the server works) and used by
the client. The PIPELINING extension reduces the number of
turn-arounds in SMTP conversations between two mailers that both
support it. This can be a big win over slow IP networks. The
implementation is based on the Internet Draft
`draft-ietf-smtpext-pipeline-02.txt' which is available from many
archive sites that have IETF documents.
* BODY [RFC1426] and SIZE [RFC1427] clauses are recognized in MAIL
FROM: SMTP commands on the receiver side. Currently it doesn't
matter whether the BODY type is 7BIT or 8BITMIME - Smail is always
8-bit transparent. If the value passed in a SIZE clause exceeds
max_message_size, a "552 message too large" error reply is
generated.
* The sender side tries to greet the receiving SMTP with an EHLO
command. If this fails, we send the standard HELO greeting. If
this also fails, we try RSET to clear up the remote mailer and then
HELO. If the EHLO is accepted, Smail parses the response to find
out whether the receiver understands SIZE and BODY. A maximum
message size that is provided by the receiving SMTP is currently
ignored, but the MAIL FROM: command is decorated with a SIZE
declaration based on a guess of the spooled message's size. The
BODY clause is never issued.
* If HAVE_DF_SPOOL is defined, Smail will try to dynamically find out
how much space is available for spooling incoming files. The
announced SIZE will be limited by available disk space in addition
to the administrative limit imposed by max_message_size. The code
that computes free disk space needs either the statvfs() or the
statfs() call. HAVE_STATVFS should be defined on systems that have
statvfs(). If your system doesn't have statvfs(), HAVE_SYS_STATFS_H
should be defined if you have the <sys/statfs.h> header.
* The SMTP greeting line (smtp_banner) now has a different default
value when HAVE_EHLO is defined. The new greeting is two lines
long, and the second line starts with the string "ESMTP". It seems
that this is necessary to activate ESMTP in a sending SMTP agent
that is based on Sendmail 8. The two-line greeting (although
perfectly legal according to the RFCs) *might* cause problems with
legacy mailers. Fortunately I haven't found such a case yet. In
case you have problems with other mailers not being able to connect
to yours, you can always override the `smtp_banner' variable in one
of Smail's configuration files. I would also be interested in
hearing about such broken mailers if they exist.
* NEW (December 13, 1994): The XVRB, XONE and XQUE extensions are
recognized and logged. Some Sendmail variants support these. Only
the XVRB extension is used when debugging, in order to get more
messages from a receiving sendmail.
* NEW (December 13, 1994): When the receiving SMTP is broken so that
it drops the line after we have sent EHLO, then the connection will
now be retried immediately without ESMTP negotiation. This case
used to be interpreted as a normal line failure, so that messages to
such mailers would be retried for a long time and eventually bounced.
INSTALLATION
------------
Apply the enclosed patch to the top-level directory of the Smail
distribution (the one that contains the src/ subdirectory). You will
need a fairly recent version of the "patch" program because the patch
is in the newer, more compact and more readable Unidiff format.
The server side of the extensions cannot be disabled in this version.
This means that EHLO is always accepted as an SMTP greeting, and the
SIZE parameter is accepted.
In order to enable the extensions, the following symbols can be added
to the HAVE variable in the EDITME file:
EHLO enables client-side support for EHLO, SIZE and
PIPELINING
ESMTP_8BITMIME
to enable minimal server-side RFC 1426 support.
Basically the BODY clause is accepted and ignored.
DF_SPOOL
causes free disk space to be checked dynamically when
determining the maximum size of messages that will be
accepted. This needs other symbols such as
HAVE_STATVFS or HAVE_SYS_STAT_H to be defined in turn.
CAVEATS
-------
When an ESMTP-capable client Smail talks to a non-ESMTP-capable SMTP
server, there are two additional query/reply pairs for each SMTP
connection. This is because Smail issues EHLO to find out whether the
recipient mailer handles ESMTP, gets an error message and then sends
an RSET before retrying with HELO. If you have extremely slow or
unreliable SMTP connections, or if your network connections are
charged by the packet this may matter to you.
TODO
----
Right now the SMTP extensions don't really give any new functionality,
except that max_message_size is *somewhat* enforced, but only for
cooperating (RFC 1427-savvy) client mailers. The following
improvements to the code might be useful:
* Smail should be extended to handle 8-bit text messages in a more
standard way. Currently it is simply 8-bit transparent, which is
seen as a feature by most users. However, sending non-7-bit-ASCII
characters to remote mailer daemons is a violation of RFC 821 and
can lead to interoperability problems. Even when communicating over
8-bit clean SMTP networks, the "Just-Send-8" approach has the
problem that it is not clear which character set has been used by
the author [RFC1428].
In principle, all these problems are solved by the MIME standard
[RFC1341, RFC1342], which is rapidly gaining acceptance. In a world
where everybody uses MIME-capable mailers, there wouldn't be any
eighth bits to be transported. But as long as people still send
8-bit messages, Smail could convert such messages to 7bit-MIME when
they are transported over an SMTP network. RFC1428 describes how
this could be done.
* If the "link broken by EHLO" problem really does occur in the real
world, it has to be handled by Smail's SMTP client side. For
instance, once an EHLO command causes a link to be broken, the
receiving host should be noted in a "black list" by a mechanism
similar to the retry file mechanism. Hosts should be removed from
the black list periodically, so that when they are upgraded to
SMTP-conforming software, the new functionality will be used. It
would also be nice if the "retry" file could be extended by
something like "ehlo/noehlo" keyword that would allow specific
hosts/domains to be excluded from ESMTP trials.
ReFerenCes
1344 N. Borenstein, "Implications of MIME for Internet Mail Gateways",
06/11/1992. (Pages=9) (Format=.txt, .ps)
1425 J. Klensin, N. Freed, M. Rose, E. Stefferud, D. Crocker, "SMTP
Service Extensions", 02/10/1993. (Pages=10) (Format=.txt)
1426 J. Klensin, N. Freed, M. Rose, E. Stefferud, D. Crocker, "SMTP
Service Extension for 8bit-MIMEtransport", 02/10/1993. (Pages=6)
(Format=.txt)
1427 K. Moore, N. Freed, J. Klensin, "SMTP Service Extension for
Message Size Declaration", 02/10/1993. (Pages=8) (Format=.txt)
1428 G. Vaudreuil, "Transition of Internet Mail from Just-Send-8 to
8Bit-SMTP/MIME", 02/10/1993. (Pages=6) (Format=.txt)
1521 N. Borenstein, N. Freed, "MIME (Multipurpose Internet Mail
Extensions) Part One: Mechanisms for Specifying and Describing
the Format of Internet Message Bodies", 09/23/1993. (Pages=81)
(Format=.txt) (Obsoletes RFC1341)
1522 K. Moore, "MIME (Multipurpose Internet Mail Extensions) Part Two:
Message Header Extensions for Non-ASCII Text",
09/23/1993. (Pages=10) (Format=.txt) (Obsoletes RFC1342)
--- src/Makefile 1994/11/14 09:19:33 1.1
+++ src/Makefile 1994/11/29 18:30:03
@@ -1,5 +1,5 @@
#!/bin/make -f
-# @(#) $Id: Makefile,v 1.1 1994/11/14 09:19:33 logiciel Exp $
+# @(#) $Id: Makefile,v 1.2 1994/11/29 18:30:03 logiciel Exp $
# Makefile for the smail program
#
# Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
@@ -167,6 +167,10 @@
(cd $$i; ${MK} SRC_PREFIX=${SRC_PREFIX}$$i/ depend); \
done
+iobpeek.h: ${DEFS_SH}
+ @rm -f iobpeek.h
+ @. ./${DEFS_SH}; ${XEXEC} ${SHELL} geniobpeek.sh $$CC "$$CFLAGS"
+
depend: ${SRC} check_defs subdir_depend local_depend
subdir_depend: remove_driver_makefiles \
@@ -303,5 +307,6 @@
# special dependency:
version.o: version.h
+smtprecv.o: iobpeek.h
# DO NOT REMOVE THIS LINE, OR "make depend" WILL NOT WORK
--- src/addr.c 1994/11/14 09:20:09 1.1
+++ src/addr.c 1994/11/29 18:31:32
@@ -1,4 +1,4 @@
-/* @(#) $Id: addr.c,v 1.1 1994/11/14 09:20:09 logiciel Exp $ */
+/* @(#) $Id: addr.c,v 1.2 1994/11/29 18:31:31 logiciel Exp $ */
/*
* Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
@@ -63,6 +63,16 @@
char *address; /* address to be preparsed */
char **error; /* return error message here */
{
+ char * ignore;
+ return preparse_address_1(address, error, & ignore);
+}
+
+char *
+preparse_address_1(address, error, rest)
+ char *address;
+ char **error;
+ char **rest;
+{
register char *ap; /* temp for scanning address */
char *mark_start = NULL; /* marked position of < */
char *mark_end = NULL; /* marked position of > */
@@ -125,9 +135,10 @@
ap = xmalloc((unsigned)(strlen(address) + 1));
(void) strcpy(ap, address);
if (mark_end) {
- *mark_end = '>'; /* widden the original address */
+ *mark_end++ = '>'; /* widden the original address */
}
DEBUG1(DBG_ADDR_HI, "preparse_address returns: %s\n", ap);
+ *rest = mark_end;
return ap; /* no transformations */
}
@@ -144,9 +155,10 @@
ap = xmalloc((unsigned)(strlen(address) + 1));
(void) strcpy(ap, address);
if (mark_end) {
- *mark_end = '>'; /* widden the original address */
+ *mark_end++ = '>'; /* widden the original address */
}
DEBUG1(DBG_ADDR_HI, "preparse address returns: %s\n", ap);
+ *rest = mark_end;
return ap; /* address should be okay */
}
ap++;
@@ -157,7 +169,7 @@
/* first part already !-route */
(void) strncpy(p, address, ap-address);
if (mark_end) {
- *mark_end = '>'; /* widden the original address */
+ *mark_end++ = '>'; /* widden the original address */
}
ap = build_uucp_route(ap, error); /* build !-route */
if (ap == NULL) {
@@ -167,15 +179,17 @@
(void) strcat(p, ap); /* concatenate together */
xfree(ap);
DEBUG1(DBG_ADDR_HI, "preparse_address returns: %s\n", p);
+ *rest = mark_end;
return p; /* transformed */
}
}
ap = xmalloc((unsigned)(strlen(address) + 1));
(void) strcpy(ap, address);
if (mark_end) {
- *mark_end = '>'; /* widden the original address */
+ *mark_end++ = '>'; /* widden the original address */
}
DEBUG1(DBG_ADDR_HI, "preparse address returns: %s\n", ap);
+ *rest = mark_end;
return ap; /* no transformations */
}
--- src/config.h 1994/11/14 09:53:56 1.1
+++ src/config.h 1994/11/29 18:32:51
@@ -1,4 +1,4 @@
-/* @(#) $Id: config.h,v 1.1 1994/11/14 09:53:56 logiciel Exp $ */
+/* @(#) $Id: config.h,v 1.2 1994/11/29 18:32:50 logiciel Exp $ */
/*
* Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
@@ -663,7 +663,11 @@
* SMTP startup banner message
*/
#ifndef SMTP_BANNER
-# define SMTP_BANNER "$primary_name Smail$version #$compile_num ready at $date"
+# ifdef HAVE_EHLO
+# define SMTP_BANNER "$primary_name Smail$version #$compile_num ready at $date\nESMTP supported"
+# else
+# define SMTP_BANNER "$primary_name Smail$version #$compile_num ready at $date"
+# endif
#endif
/*
@@ -939,7 +943,11 @@
# ifdef SMALL_MEMORY
# define MESSAGE_BUF_SIZE BUFSIZ
# else
-# define MESSAGE_BUF_SIZE MAX_MESSAGE_SIZE
+# if MAX_MESSAGE_SIZE == 0
+# define MESSAGE_BUF_SIZE (256*1024)
+# else
+# define MESSAGE_BUF_SIZE MAX_MESSAGE_SIZE
+# endif
# endif
#endif
--- src/extern.h 1994/11/14 09:23:47 1.1
+++ src/extern.h 1994/11/29 18:33:22
@@ -1,4 +1,4 @@
-/* @(#) $Id: extern.h,v 1.1 1994/11/14 09:23:47 logiciel Exp $ */
+/* @(#) $Id: extern.h,v 1.2 1994/11/29 18:33:22 logiciel Exp $ */
/*
* Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
@@ -237,6 +237,7 @@
*/
/* external functions defined in addr.c */
extern char *preparse_address();
+extern char *preparse_address_1();
extern int parse_address();
extern char *address_token();
extern char *back_address_token();
@@ -460,6 +461,7 @@
extern int new_grade();
extern void defer_message();
extern long message_date();
+extern long spool_max_free_space();
/* external functions defined in string.c */
extern int strcmpic();
--- src/smtprecv.c 1994/11/14 09:24:09 1.1
+++ src/smtprecv.c 1994/11/30 15:10:35
@@ -1,4 +1,4 @@
-/* @(#) $Id: smtprecv.c,v 1.1 1994/11/14 09:24:09 logiciel Exp $ */
+/* @(#) $Id: smtprecv.c,v 1.3 1994/11/30 15:10:34 logiciel Exp $ */
/*
* Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
@@ -22,6 +22,7 @@
#include "dys.h"
#include "log.h"
#include "hash.h"
+#include "iobpeek.h"
#ifndef DEPEND
# include "extern.h"
# include "debug.h"
@@ -45,6 +46,7 @@
/* types local to this file */
enum e_smtp_commands {
HELO_CMD, /* HELO domain */
+ EHLO_CMD, /* EHLO domain */
MAIL_CMD, /* MAIL FROM:<sender> */
RCPT_CMD, /* RCPT TO:<recipient> */
DATA_CMD, /* DATA */
@@ -60,15 +62,33 @@
};
/* functions local to this file */
+#ifdef __STDC__
+static void reset_state(void);
+static enum e_smtp_commands read_smtp_command(FILE *, FILE *);
+static void expand_addr(char *, FILE *);
+static int verify_addr(char *, FILE *, int);
+static void smtp_input_signals(void);
+static void smtp_processing_signals(void);
+static void set_term_signal(int);
+static void smtp_receive_timeout_sig(int);
+static void smtp_sig_unlink(int);
+#ifdef HAVE_DF_SPOOL
+static long compute_max_message_size_from_df_spool (void);
+#endif
+#else /* not __STDC__ */
static void reset_state();
static enum e_smtp_commands read_smtp_command();
static void expand_addr();
-static void verify_addr();
+static int verify_addr();
static void smtp_input_signals();
static void smtp_processing_signals();
static void set_term_signal();
static void smtp_receive_timeout_sig();
static void smtp_sig_unlink();
+#ifdef HAVE_DF_SPOOL
+static long compute_max_message_size_from_df_spool ();
+#endif
+#endif /* not __STDC__ */
/* variables local to this file */
static char *data; /* interesting data within input */
@@ -80,6 +100,7 @@
"250-The following SMTP commands are recognized:",
"250-",
"250- HELO hostname - startup and give your hostname",
+ "250- EHLO hostname - startup with extension info",
"250- MAIL FROM:<sender-address> - start transaction from sender",
"250- RCPT TO:<recipient-address> - name recipient for message",
"250- VRFY <address> - verify deliverability of address",
@@ -98,6 +119,15 @@
"250 Multiple messages may be specified. End the last one with a QUIT."
};
+typedef enum {
+ BF_unspecified,
+ BF_7bit,
+ BF_8bitmime
+}
+BodyFormat;
+
+#define BS_UNSPECIFIED -1L
+typedef long BodySize;
/*
@@ -132,6 +162,9 @@
FILE *save_errfile = errfile;
int save_debug = debug;
int temp, i, c;
+ char *rest;
+ int ehlo_p = 0;
+ long accepted_msg_size = max_message_size;
/* initialize state */
initialize_state();
@@ -205,11 +238,14 @@
if (out) {
alarm(smtp_receive_command_timeout);
}
- switch (read_smtp_command(in)) {
+ switch (read_smtp_command(in, out)) {
+ case EHLO_CMD:
+ ehlo_p = 1;
case HELO_CMD:
strip_rfc822_comments(data);
if (out && data[0] == '\0') {
- fprintf(out, "501 HELO requires domain name as operand\r\n");
+ fprintf(out, "501 %s requires domain name as operand\r\n",
+ ehlo_p ? "EHLO" : "HELO");
fflush(out);
break;
}
@@ -217,38 +253,74 @@
sender_host = COPY_STRING(data);
}
if (sender_proto == NULL) {
- sender_proto = (out? "smtp": "bsmtp");
+ sender_proto = (out? ehlo_p? "esmtp": "smtp"
+ : ehlo_p? "ebsmtp": "bsmtp");
}
+ if (! ehlo_p) {
if (out) {
fprintf(out, "250 %s Hello %s\r\n", primary_name, data);
fflush(out);
}
reset_state();
break;
+ }
+ strip_rfc822_comments(data);
+ if (out && data[0] == '\0') {
+ fprintf(out, "501 EHLO requires domain name as operand\r\n");
+ fflush(out);
+ break;
+ }
+ if (sender_host == NULL && data[0] != '\0') {
+ sender_host = COPY_STRING(data);
+ }
+ if (sender_proto == NULL) {
+ sender_proto = (out? "smtp": "bsmtp");
+ }
+ if (out) {
+ fprintf(out, "250-%s Hello %s, here's what we support:\r\n",
+ primary_name, data);
+#ifndef NO_VERIFY
+ fprintf(out, "250-EXPN\r\n");
+#endif
+#ifdef HAVE_DF_SPOOL
+ accepted_msg_size = compute_max_message_size_from_df_spool ();
+ if (accepted_msg_size == -1
+ || (max_message_size && max_message_size < accepted_msg_size))
+ accepted_msg_size = max_message_size;
+#endif
+ if (accepted_msg_size && accepted_msg_size != -1) {
+ fprintf(out, "250-SIZE %d\r\n", accepted_msg_size);
+ } else {
+ fprintf(out, "250-SIZE\r\n");
+ }
+#ifdef HAVE_ESMTP_8BITMIME
+ fprintf(out, "250-8BITMIME\r\n");
+#endif
+ fprintf(out, "250-PIPELINING\r\n");
+ fprintf(out, "250 HELP\r\n");
+ fflush(out);
+ }
+ reset_state();
+ break;
case MAIL_CMD:
strip_rfc822_comments(data);
if (out && data[0] == '\0') {
fprintf(out, "501 MAIL FROM requires address as operand\r\n");
- fflush(out);
break;
}
if (sender) {
if (out) {
fprintf(out, "503 Sender already specified\r\n");
- fflush(out);
}
break;
}
- sender = preparse_address(data, &error);
+ sender = preparse_address_1(data, &error, &rest);
if (out) {
- if (sender) {
- fprintf(out, "250 <%s> ... Sender Okay\r\n",
- sender);
- } else {
+ if (!sender) {
fprintf(out, "501 <%s> ... %s\r\n", data, error);
+ break;
}
- fflush(out);
}
if (sender && sender[0] == '\0') {
/* special error sender form <> given */
@@ -258,26 +330,143 @@
/* special smail-internal <+> was given */
sender = COPY_STRING("<+>");
}
+ {
+ char * format = 0;
+ int format_length = 0;
+ BodyFormat body_format = BF_unspecified;
+ BodySize body_size = BS_UNSPECIFIED;
+
+ while (rest && *rest != '\0') {
+ /* maybe we have an extended MAIL command */
+ while (*rest != '\0' && isspace (*rest)) {
+ ++rest;
+ }
+ {
+ int restlen = 0;
+ while (*(rest+restlen) != 0
+ && !isspace (*(rest+restlen))
+ && *(rest+restlen) != '=') {
+ ++restlen;
+ }
+ if (strncmpic(rest, "SIZE", restlen) == 0) {
+ rest += restlen;
+ if (*rest != '=') {
+ if (out) {
+ *(rest+restlen) = '\0';
+ fprintf(out, "555 missing SIZE parameter\r\n",
+ rest);
+ }
+ goto fail_mail_cmd;
+ }
+ ++rest;
+ restlen = 0;
+ body_size = 0;
+ while (*(rest+restlen) != 0
+ && isdigit (*(rest+restlen))
+ && (!accepted_msg_size || body_size <= accepted_msg_size)) {
+ body_size = 10*body_size+*(rest+restlen)-'0';
+ ++restlen;
+ }
+ if (accepted_msg_size && body_size > accepted_msg_size) {
+ if (out) {
+ fprintf(out, "552 message too large\r\n");
+ }
+ goto fail_mail_cmd;
+ } else if (*(rest+restlen) != 0
+ && !isspace (*(rest+restlen))) {
+ if (out) {
+ while (*(rest+restlen) != 0
+ && !isspace (*(rest+restlen))) {
+ ++restlen;
+ }
+ *(rest+restlen) = '\0';
+ fprintf(out, "555 malformed SIZE clause %s\r\n",
+ rest);
+ }
+ goto fail_mail_cmd;
+ }
+ } else
+#ifdef HAVE_ESMTP_8BITMIME
+ if (strncmpic(rest, "BODY", restlen) == 0) {
+ rest += restlen;
+ if (*rest != '=') {
+ if (out) {
+ *(rest+restlen) = '\0';
+ fprintf(out, "555 missing BODY parameter\r\n",
+ rest);
+ }
+ goto fail_mail_cmd;
+ }
+ ++rest;
+ restlen = 0;
+ while (*(rest+restlen) != 0
+ && !isspace (*(rest+restlen))) {
+ ++restlen;
+ }
+ if (strncmpic(rest, "7BIT", restlen) == 0) {
+ body_format = BF_7bit;
+ } else if (strncmpic(rest, "8BITMIME", restlen) == 0) {
+ body_format = BF_8bitmime;
+ } else {
+ if (out) {
+ *(rest+restlen) = '\0';
+ fprintf(out, "555 unknown BODY type %s\r\n",
+ rest);
+ }
+ goto fail_mail_cmd;
+ }
+ } else
+#endif
+ {
+ if (out) {
+ *(rest+restlen) = '\0';
+ fprintf(out, "555 Unknown MAIL TO: option %s\r\n", rest);
+ }
+ goto fail_mail_cmd;
+ }
+ rest += restlen;
+ }
+ }
+ if (out) {
+ if (format && *format != '\0') {
+ *(format+format_length) = '\0';
+ fprintf(out, "250 <%s> ... Sender Okay, using format %s\r\n",
+ sender, format);
+ } else {
+ fprintf(out, "250 <%s> ... Sender Okay\r\n",
+ sender);
+ }
+ }
+ }
+ break;
+ fail_mail_cmd:
+ if (sender) {
+ xfree(sender);
+ sender = NULL;
+ }
break;
case RCPT_CMD:
strip_rfc822_comments(data);
if (out && data[0] == '\0') {
fprintf(out, "501 RCPT TO requires address as operand\r\n");
- fflush(out);
break;
}
cur = alloc_addr();
if (out) {
+#ifdef VERIFY_RCPTS
+ if (! verify_addr (data, out, 1)) {
+ break;
+ }
+#else /* not VERIFY_RCPTS */
if (cur->work_addr = preparse_address(data, &error)) {
fprintf(out, "250 <%s> ... Recipient Okay\r\n",
cur->work_addr);
- fflush(out);
} else {
fprintf(out, "501 <%s> ... %s\r\n", data, error);
- fflush(out);
break;
}
+#endif
}
/*
* surround in angle brackets, if the addr begins with `-'.
@@ -404,7 +593,7 @@
fprintf(out, "502 Command not implemented\r\n");
#else
strip_rfc822_comments(data);
- verify_addr(data, out);
+ verify_addr(data, out, 0);
fflush(out);
#endif
}
@@ -438,7 +627,6 @@
reset_state();
if (out) {
fprintf(out, "250 Reset state\r\n");
- fflush(out);
}
break;
@@ -549,18 +737,22 @@
}
static enum e_smtp_commands
-read_smtp_command(f)
+read_smtp_command(f, out)
register FILE *f; /* SMTP command stream */
+ register FILE *out; /* output, may have to be flushed */
{
static struct str input; /* buffer storing recent command */
static int inited = FALSE; /* TRUE if input initialized */
register int c; /* input char */
+ int flushed_p = !out;
+
static struct smtp_cmd_list {
char *name;
int len;
enum e_smtp_commands cmd;
} smtp_cmd_list[] = {
"HELO", sizeof("HELO")-1, HELO_CMD,
+ "EHLO", sizeof("EHLO")-1, EHLO_CMD,
"MAIL FROM:", sizeof("MAIL FROM:")-1, MAIL_CMD,
"RCPT TO:", sizeof("RCPT TO:")-1, RCPT_CMD,
"DATA", sizeof("DATA")-1, DATA_CMD,
@@ -580,7 +772,15 @@
} else {
input.i = 0;
}
- while ((c = getc(f)) != '\n' && c != EOF) {
+ for (;;) {
+ if (!flushed_p && IOB_MAYBE_EMPTY_P (f)) {
+ ++flushed_p;
+ fflush (out);
+ }
+ c = getc(f);
+ if (c == EOF || c == '\n') {
+ break;
+ }
STR_NEXT(&input, c);
}
if (input.p[input.i - 1] == '\r') {
@@ -665,24 +865,26 @@
*
* redisplay the input address if it is a valid address.
*/
-static void
-verify_addr(in_addr, out)
+static int
+verify_addr(in_addr, out, rcpt_p)
char *in_addr; /* input address string */
FILE *out; /* write expansion here */
+ int rcpt_p; /* non-zero if called from RCPT */
{
struct addr *addr = alloc_addr(); /* get an addr structure */
struct addr *okay = NULL; /* verified address */
struct addr *defer = NULL; /* temporarily unverifiable addr */
struct addr *fail = NULL; /* unverified addr */
char *error; /* hold error message */
+ int error_code; /* reply code for negative result */
+ error_code = rcpt_p ? 501 : 550;
addr->in_addr = in_addr; /* setup the input addr structure */
/* build the mungeable addr string */
addr->work_addr = preparse_address(in_addr, &error);
if (addr->work_addr == NULL) {
- fprintf(out, "501 %s ... %s\r\n", in_addr, error);
- fflush(out);
- return;
+ fprintf(out, "%d %s ... %s\r\n", error_code, in_addr, error);
+ return 0;
}
/* cache directors and routers on the assumption we will need them again */
@@ -698,15 +900,20 @@
if (okay) {
fprintf(out, "250 %s\r\n", in_addr);
+ return 1;
} else if (defer) {
- fprintf(out, "550 %s ... cannot verify: %s\r\n", in_addr,
- defer->error->message);
+ fprintf(out, "%d %s ... cannot verify: %s\r\n",
+ rcpt_p ? 250 : 550, in_addr, defer->error->message);
+ return 1;
} else if (fail) {
- fprintf(out, "550 %s ... not matched: %s\r\n", in_addr,
- fail->error->message);
+ fprintf(out, "%d %s ... not matched: %s\r\n",
+ error_code, in_addr, fail->error->message);
+ return 0;
} else {
/* hmmm, it should have been in one of the lists */
- fprintf(out, "550 %s ... not matched\r\n", in_addr);
+ fprintf(out, "%d %s ... not matched\r\n",
+ error_code, in_addr);
+ return 0;
}
}
#endif /* NO_VERIFY */
@@ -806,3 +1013,17 @@
unlink_spool();
exit(EX_OSFILE);
}
+
+#ifdef HAVE_DF_SPOOL
+static long
+compute_max_message_size_from_df_spool (void)
+{
+ long free_bytes = spool_max_free_space ();
+ const long reserved = 2*1024*1024;
+ const long min_max_message_size = 20*1024;
+
+ if (free_bytes == -1)
+ return free_bytes;
+ return free_bytes < 2*reserved ? -1 : free_bytes - reserved;
+}
+#endif
--- src/spool.c 1994/11/14 16:53:48 1.1
+++ src/spool.c 1994/11/30 08:45:19
@@ -1,4 +1,4 @@
-/* @(#) $Id: spool.c,v 1.1 1994/11/14 16:53:48 logiciel Exp $ */
+/* @(#) $Id: spool.c,v 1.2 1994/11/30 08:45:18 logiciel Exp $ */
/*
* Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
@@ -1622,6 +1622,63 @@
return clock;
}
+
+#ifdef HAVE_DF_SPOOL
+#ifdef HAVE_STATVFS
+#include <sys/statvfs.h>
+#else /* not HAVE_STATVFS */
+#ifdef HAVE_SYS_STATFS_H
+#include <sys/statfs.h>
+#else /* not HAVE_SYS_STATFS_H */
+#include <sys/vfs.h>
+#endif /* not HAVE_SYS_STATFS_H */
+#endif /* not HAVE_STATVFS */
+
+long
+spool_max_free_space ()
+{
+ char *dirs = spool_dirs;
+ char dirname[SIZE_FILNAM];
+ char *p;
+ long max_free_bytes = -1, free_bytes;
+ int result;
+#ifdef HAVE_STATVFS
+ struct statvfs buf;
+#else
+ struct statfs buf;
+#endif
+
+ while (dirs && *dirs) {
+ for (p = dirname; *dirs && *dirs != ':'; *p++ = *dirs++) ;
+ if (*dirs == ':') {
+ /* next try gets the next directory */
+ dirs++;
+ }
+ *p++ = '\0'; /* terminate directory name */
+#ifdef HAVE_STATVFS
+ result = statvfs (dirname, &buf);
+#else
+ result = statfs (dirname, &buf, sizeof buf, 0);
+#endif
+ if (result == -1)
+ continue;
+#ifdef HAVE_STATVFS
+ free_bytes = buf.f_bfree * buf.f_frsize;
+#else
+ free_bytes = buf.f_bfree * buf.f_bsize;
+#endif
+ if (free_bytes > max_free_bytes)
+ max_free_bytes = free_bytes;
+ }
+ return max_free_bytes;
+}
+#else /* not HAVE_DF_SPOOL */
+long
+spool_max_free_space ()
+{
+ return -1;
+}
+#endif /* not HAVE_DF_SPOOL */
#ifdef STANDALONE
--- src/transports/smtplib.c 1994/11/14 09:32:24 1.1
+++ src/transports/smtplib.c 1994/12/13 11:39:29
@@ -1,4 +1,4 @@
-/* @(#) $Id: smtplib.c,v 1.1 1994/11/14 09:32:24 logiciel Exp $ */
+/* @(#) $Id: smtplib.c,v 1.5 1994/12/13 11:39:28 logiciel Exp $ */
/*
* Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
@@ -30,6 +30,9 @@
# include "../error.h"
# include "../debug.h"
#endif
+#if !defined(NO_LOG_EHLO)
+# include "../log.h"
+#endif
#ifdef ANSI_C
# define P_(x) x
@@ -40,6 +43,7 @@
#endif
/* supported SMTP commands */
+#define EHLO(domain) "EHLO %s", domain
#define HELO(domain) "HELO %s", domain
#define MAIL_BEGIN "MAIL FROM:<"
#define MAIL_END ">"
@@ -48,8 +52,10 @@
#define DATA "DATA"
#define DATA_END "."
#define QUIT "QUIT"
+#define VERB "VERB"
/* reply code groups, encoded in hex */
+#define POSITIVE_DEBUG 0x000 /* debugging messages */
#define POSITIVE_PRELIM 0x100 /* positive preliminary replies */
#define POSITIVE_COMPLETE 0x200 /* positive completion replies */
#define POSITIVE_INTERMEDIATE 0x300 /* positive intermediate replies */
@@ -69,6 +75,8 @@
/* pseudo-reply codes */
#define REPLY_PROTO_ERROR 0x498 /* protocol error on read */
#define REPLY_TIMEOUT 0x499 /* timeout on read, or EOF on read */
+#define REPLY_NOT_ACCEPTABLE 0x501 /* request not acceptable */
+#define REPLY_SEQUENCE_ERROR 0x503 /* Bad sequence of commands */
/* variables local to this file */
static struct str smtp_out; /* region for outgoing commands */
@@ -77,18 +85,45 @@
static JUMP_ENVBUF timeout_buf; /* timeouts jump here */
/* functions local to this file */
+#ifdef __STDC__
+extern int smtp_startup(struct smtp *, struct error **, int);
+extern int smtp_send(struct smtp *, struct addr *, struct addr **, struct addr **, struct addr **, struct error **);
+extern void smtp_shutdown(struct smtp *);
+static void do_smtp_shutdown(struct smtp *, int);
+static int write_command_maybe_wait(struct smtp *, unsigned, char *, int, char **);
+static int wait_write_command(struct smtp *, unsigned, char *, int, char **);
+static int write_command_nowait(struct smtp *, char *, int);
+static int flush_command_stream(struct smtp *, char **);
+static int wait_read_response(struct smtp *, unsigned, char **);
+static int read_response_internal(struct smtp *, unsigned, char **);
+static void catch_timeout(void);
+static struct error * no_remote(struct transport *, char *);
+static struct error * try_again(struct transport *, char *);
+static struct error * fatal_error(struct transport *, char *);
+static struct error * remote_full(struct transport *, char *);
+static struct error * remote_bad_address(struct transport *, char *);
+static struct error * write_failed(struct transport *);
+static struct error * read_failed(struct transport *);
+#else /* not __STDC__ */
+extern int smtp_startup();
+extern int smtp_send();
+extern void smtp_shutdown();
static void do_smtp_shutdown();
+static int write_command_maybe_wait();
static int wait_write_command();
+static int write_command_nowait();
+static int flush_command_stream();
static int wait_read_response();
+static int read_response_internal();
static void catch_timeout();
-static struct error *no_remote();
-static struct error *try_again();
-static struct error *fatal_error();
-static struct error *remote_full();
-static struct error *remote_bad_address();
-static struct error *write_failed();
-static struct error *read_failed();
-
+static struct error * no_remote();
+static struct error * try_again();
+static struct error * fatal_error();
+static struct error * remote_full();
+static struct error * remote_bad_address();
+static struct error * write_failed();
+static struct error * read_failed();
+#endif /* not __STDC__ */
/*
* smtp_startup - initiate contact on an SMTP connection
@@ -97,20 +132,31 @@
* the session for future mail commands. Once the startup has been
* acomplished, smtp_send() can be used to send individual messages.
*
+ * If try_ehlo is non-zero and ESMTP support is configured, perform
+ * ESMTP negotiaition using the EHLO greeting command.
+ *
* return:
* SMTP_SUCCEED on successful startup
* SMTP_FAIL if the connection should not be retried
* SMTP_AGAIN if the connection should be retried later
+ * SMTP_EHLO_FAIL if the receiver hung up in response
+ * to an EHLO command. This can only occur when
+ * try_ehlo is set.
*
- * For SMTP_FAIL and SMTP_AGAIN, return a filled-in error structure.
+ * For SMTP_FAIL and SMTP_AGAIN (but not SMTP_EHLO_FAIL), return a
+ * filled-in error structure.
*/
int
-smtp_startup(smtpb, error_p)
+smtp_startup(smtpb, error_p, try_ehlo)
struct smtp *smtpb; /* SMTP description block */
struct error **error_p; /* error description */
+ int try_ehlo; /* whether to use ESMTP */
{
int reply;
char *reply_text;
+#ifdef HAVE_EHLO
+ int tried_rset = 0;
+#endif
if (! smtp_init_flag) {
STR_INIT(&smtp_in);
@@ -134,6 +180,134 @@
return SMTP_AGAIN;
}
+#ifdef HAVE_EHLO
+ if (try_ehlo) {
+ /*
+ * say who we are.
+ * Possible responses:
+ * 250 - okay (continue conversation)
+ * 421 - closing down (try again later)
+ * 5xx - fatal error (try HELO)
+ */
+ smtp_out.i = 0;
+ (void) str_printf(&smtp_out, EHLO(primary_name));
+
+ reply = wait_write_command(smtpb, smtpb->short_timeout,
+ smtp_out.p, smtp_out.i, &reply_text);
+
+ if (reply == REPLY_TIMEOUT) {
+ /* Some gateways just terminate the conection when they
+ receive an EHLO. We handle this case by returning a
+ special value here. Also write a message to the log
+ file because it is interesting to know which mailers
+ expose this problem. */
+ write_log(LOG_SYS, "link broken by EHLO!");
+ return SMTP_EHLO_FAIL;
+ }
+ if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
+ /* remote SMTP closed, try again later */
+ *error_p = try_again(smtpb->tp, reply_text);
+ return SMTP_AGAIN;
+ }
+ if (reply == REPLY_OK) {
+ char * cp = reply_text;
+ int on_greet_line = 1;
+
+ smtpb->smtp_flags = ESMTP_basic;
+ /* Parse the EHLO reply to find out
+ what the remote server supports */
+ while (*cp != 0) {
+ int skip;
+ int keywordlength;
+
+ for (skip = 4; *cp != 0 && skip; --skip, ++cp) {
+ if (skip == 1
+ ? (*cp != ' ' && *cp != '-')
+ : (! isdigit (*cp))) {
+ goto malformed_ehlo_reply;
+ }
+ }
+ if (skip != 0) {
+ goto malformed_ehlo_reply;
+ }
+ if (on_greet_line) {
+ /*
+ * Ignore greeting on first line
+ */
+ on_greet_line = 0;
+ goto skip_rest_of_line;
+ }
+ for (keywordlength = 0;
+ *(cp+keywordlength) != 0
+ && !isspace (*(cp+keywordlength));
+ ++keywordlength)
+ ;
+ if (strncmpic(cp, "SIZE", keywordlength) == 0) {
+ unsigned long max_size = 0;
+
+ cp += keywordlength;
+ while (*cp == ' ' || *cp == '\t')
+ ++cp;
+ if (!isdigit(*cp) && *cp != '\n')
+ goto malformed_ehlo_reply;
+ while (isdigit(*cp)) {
+ max_size *= 10;
+ max_size += *cp - '0';
+ ++cp;
+ }
+ smtpb->smtp_flags |= ESMTP_size;
+ smtpb->max_size = max_size;
+ } else if (strncmpic(cp, "8BITMIME", keywordlength) == 0) {
+ smtpb->smtp_flags |= ESMTP_8bitmime;
+ } else if (strncmpic(cp, "PIPELINING", keywordlength) == 0) {
+ smtpb->smtp_flags |= ESMTP_pipelining;
+ } else if (strncmpic(cp, "XVRB", keywordlength) == 0) {
+ smtpb->smtp_flags |= ESMTP_verbose;
+ } else if (strncmpic(cp, "XONE", keywordlength) == 0) {
+ smtpb->smtp_flags |= ESMTP_one;
+ } else if (strncmpic(cp, "XQUE", keywordlength) == 0) {
+ smtpb->smtp_flags |= ESMTP_queue;
+ } else {
+ }
+ skip_rest_of_line:
+ while (*cp != 0 && *cp != '\n')
+ ++cp;
+ if (*cp == '\n')
+ ++cp;
+ }
+#ifndef NO_LOG_EHLO
+ write_log(LOG_SYS, "destination supports esmtp%s%s%s%s%s%s",
+ smtpb->smtp_flags & ESMTP_8bitmime ? " 8BITMIME" : "",
+ smtpb->smtp_flags & ESMTP_size ? " SIZE" : "",
+ smtpb->smtp_flags & ESMTP_pipelining ? " PIPELINING" : "",
+ smtpb->smtp_flags & ESMTP_verbose ? " XVRB" : "",
+ smtpb->smtp_flags & ESMTP_one ? " XONE" : "",
+ smtpb->smtp_flags & ESMTP_queue ? " XQUE" : "");
+#endif /* not NO_LOG_EHLO */
+ if (smtpb->smtp_flags & ESMTP_verbose
+ && debug >= DBG_DRIVER_MID) {
+ smtp_out.i = 0;
+ (void) str_printf(&smtp_out, VERB);
+ reply = wait_write_command(smtpb, smtpb->short_timeout,
+ smtp_out.p, smtp_out.i, &reply_text);
+ if (REPLY_GROUP (reply) == NEGATIVE_TRY_AGAIN) {
+ /* remote SMTP closed, try again later */
+ *error_p = try_again(smtpb->tp, reply_text);
+ return SMTP_AGAIN;
+ }
+ }
+ return SMTP_SUCCEED;
+ malformed_ehlo_reply:
+ /* This seems to be a reasonable way
+ to handle malformed EHLO replies: */
+#ifndef NO_LOG_EHLO
+ write_log(LOG_SYS, "destination supports esmtp, but is buggy (%s)",
+ reply_text);
+#endif /* not NO_LOG_EHLO */
+ return SMTP_SUCCEED;
+ }
+ }
+#endif /* HAVE_EHLO */
/*
* say who we are.
* Possible responses:
@@ -141,6 +315,9 @@
* 421 - closing down (try again later)
* 5xx - fatal error (return message to sender)
*/
+#ifdef HAVE_EHLO
+ try_helo:
+#endif
smtp_out.i = 0;
(void) str_printf(&smtp_out, HELO(primary_name));
@@ -152,10 +329,47 @@
*error_p = try_again(smtpb->tp, reply_text);
return SMTP_AGAIN;
}
- if (reply != REPLY_OK) {
+ if (reply == REPLY_SEQUENCE_ERROR) {
+ /* ignore 503 Bad sequence of commands after EHLO/RSET/HELO */
+#ifndef NO_LOG_EHLO
+ write_log(LOG_SYS, "503 after EHLO/RSET/HELO (%s)",
+ reply_text);
+#endif /* not NO_LOG_EHLO */
+ } else if (reply != REPLY_OK) {
+#ifdef HAVE_EHLO
+ if (! tried_rset) {
+ /* The following */
+ /* fatal error, probably doesn't understand EHLO */
+ smtp_out.i = 0;
+ (void) str_printf(&smtp_out, "RSET");
+
+ reply = wait_write_command(smtpb, smtpb->short_timeout,
+ smtp_out.p, smtp_out.i, &reply_text);
+ if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
+ /* remote SMTP closed, try again later */
+ *error_p = try_again(smtpb->tp, reply_text);
+ return SMTP_AGAIN;
+ } else if (reply == REPLY_OK) {
+ } else {
+ /* Some gateways don't accept any commands before they get
+ a HELO, not even RSET. Fortunately it is usually safe
+ to ignore the error messages. */
+#ifndef NO_LOG_EHLO
+ write_log(LOG_SYS, "unexpected response to RSET (%s)",
+ reply_text);
+#endif /* not NO_LOG_EHLO */
+ }
+ /* the RSET command has been accepted; try again with HELO */
+ tried_rset = 1;
+ goto try_helo; /* GASP!!! */
+ } else {
+#endif /* HAVE_EHLO */
/* fatal error, return message to sender */
*error_p = fatal_error(smtpb->tp, reply_text);
return SMTP_FAIL;
+#ifdef HAVE_EHLO
+ }
+#endif
}
/* connection established */
@@ -221,11 +435,36 @@
get_sender_addr(tp)),
MAIL_END);
- reply = wait_write_command(smtpb, smtpb->long_timeout,
+ /*
+ * send (a guess of) the size of the message to be transported.
+ * Of course the guess could be a bit more educated, but usually
+ * it doesn't matter if it is slightly incorrect. We simply add
+ * 2% in order to account for \n -> \r\n conversion.
+ */
+ if (smtpb->smtp_flags & ESMTP_size) {
+ str_printf(&smtp_out, " SIZE=%lu",
+ (unsigned long) (msg_size * 1.02));
+ }
+#if 0
+ /*
+ * This is commented out because we are not supposed to send a
+ * non-MIME message on 8BITMIME mode. But sending a non-7bit
+ * clean message in 7BIT mode isn't a good idea either. Sigh.
+ * Something more sophisticated is definitely needed here.
+ */
+ if (smtpb->smtp_flags & ESMTP_8bitmime) {
+ str_printf(&smtp_out, " BODY 8BITMIME");
+ }
+#endif
+
+ /* give all of the recipient addresses to the remote SMTP */
+ okay = NULL;
+
+ reply = write_command_maybe_wait(smtpb, smtpb->long_timeout,
smtp_out.p, smtp_out.i, &reply_text);
if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
- *error_p = try_again(smtpb->tp, reply_text);
+ *error_p = try_again(tp, reply_text);
insert_addr_list(addr, defer, *error_p);
do_smtp_shutdown(smtpb, reply);
return FAIL;
@@ -253,15 +492,15 @@
str_printf(&smtp_out, "%s%s%s", RCPT_BEGIN, cur->next_addr, RCPT_END);
- reply = wait_write_command(smtpb, smtpb->long_timeout,
+ reply = write_command_maybe_wait(smtpb, smtpb->long_timeout,
smtp_out.p, smtp_out.i, &reply_text);
- if (reply == REPLY_STORAGE_FULL
- || REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN)
- {
- *error_p = (reply == REPLY_STORAGE_FULL)?
- remote_full(tp, reply_text):
- try_again(smtpb->tp, reply_text);
+ if (reply == REPLY_STORAGE_FULL) {
+ *error_p = remote_full(tp, reply_text);
+ insert_addr_list(cur, defer, *error_p);
+ next = NULL;
+ } else if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
+ *error_p = try_again(tp, reply_text);
insert_addr_list(cur, defer, *error_p);
insert_addr_list(okay, defer, *error_p);
do_smtp_shutdown(smtpb, reply);
@@ -285,9 +524,70 @@
* 4xx - remote error (try all recipients again later)
* 5xx - fatal error (return message to sender)
*/
- reply = wait_write_command(smtpb, smtpb->long_timeout,
+ reply = write_command_maybe_wait(smtpb, smtpb->long_timeout,
DATA, sizeof(DATA) - 1, &reply_text);
+ if (smtpb->smtp_flags & ESMTP_pipelining) {
+
+ if (reply != REPLY_OK) {
+ *error_p = try_again(tp, reply_text);
+ insert_addr_list(addr, defer, *error_p);
+ do_smtp_shutdown(smtpb, reply);
+ return FAIL;
+ }
+
+ if (flush_command_stream(smtpb, &reply_text) != REPLY_OK) {
+ return FAIL;
+ }
+
+ /* Now read all the responses. First the MAIL FROM: reply */
+ reply = wait_read_response(smtpb, smtpb->long_timeout,
+ &reply_text);
+ if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
+ *error_p = try_again(tp, reply_text);
+ insert_addr_list(addr, defer, *error_p);
+ do_smtp_shutdown(smtpb, reply);
+ return FAIL;
+ } else if (REPLY_GROUP(reply) == NEGATIVE_FAILED) {
+ *error_p = fatal_error(tp, reply_text);
+ insert_addr_list(addr, fail, *error_p);
+ do_smtp_shutdown(smtpb, reply);
+ return FAIL;
+ }
+
+ addr = okay;
+ okay = NULL;
+
+ for (cur = addr; cur; cur = next) {
+ next = cur->succ;
+
+ reply = wait_read_response(smtpb, smtpb->long_timeout,
+ &reply_text);
+
+ if (reply == REPLY_STORAGE_FULL) {
+ *error_p = remote_full(tp, reply_text);
+ insert_addr_list(cur, defer, *error_p);
+ next = NULL;
+ } else if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
+ *error_p = try_again(tp, reply_text);
+ insert_addr_list(cur, defer, *error_p);
+ insert_addr_list(okay, defer, *error_p);
+ do_smtp_shutdown(smtpb, reply);
+ return FAIL;
+ } else if (REPLY_GROUP(reply) == NEGATIVE_FAILED) {
+ cur->error = remote_bad_address(tp, reply_text);
+ cur->succ = *fail;
+ *fail = cur;
+ } else {
+ /* successful thus far */
+ cur->succ = okay;
+ okay = cur;
+ }
+ }
+ reply = wait_read_response(smtpb, smtpb->long_timeout,
+ &reply_text);
+ }
+
if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
*error_p = try_again(tp, reply_text);
insert_addr_list(okay, defer, *error_p);
@@ -306,7 +606,7 @@
/*
* send the message, using the hidden dot protocol.
*/
- smtpb->tp->flags |= PUT_DOTS;
+ tp->flags |= PUT_DOTS;
success = write_message(smtpb->out, tp, addr);
if (success == WRITE_FAIL) {
@@ -381,6 +681,21 @@
}
}
+static int
+write_command_maybe_wait (smtpb, timeout, text, len, reply_text)
+ struct smtp *smtpb; /* SMTP description block */
+ unsigned timeout; /* read timeout */
+ char *text; /* text of command */
+ register int len; /* length of command */
+ char **reply_text; /* text of response from remote */
+{
+ if (smtpb->smtp_flags & ESMTP_pipelining) {
+ return write_command_nowait (smtpb, text, len);
+ } else {
+ return wait_write_command (smtpb, timeout, text, len, reply_text);
+ }
+}
+
/*
* wait_write_command - send a command, then wait for the response
*
@@ -395,6 +710,30 @@
register int len; /* length of command */
char **reply_text; /* text of response from remote */
{
+ int reply;
+
+ reply = write_command_nowait (smtpb, text, len);
+ if (reply != REPLY_OK)
+ return reply;
+
+ reply = flush_command_stream (smtpb, reply_text);
+ if (reply != REPLY_OK)
+ return reply;
+
+ /* wait for the response to come back */
+ reply = wait_read_response(smtpb, timeout, reply_text);
+ return reply;
+}
+
+/*
+ * write_command_nowait - send a command
+ */
+static int
+write_command_nowait(smtpb, text, len)
+ struct smtp *smtpb; /* SMTP description block */
+ char *text; /* text of command */
+ register int len; /* length of command */
+{
register FILE *f = smtpb->out;
register char *cp;
int reply;
@@ -421,19 +760,22 @@
for (cp = smtpb->nl; *cp; cp++) {
putc(*cp, f);
}
+ return REPLY_OK;
+}
+
+static int
+flush_command_stream(smtpb, reply_text)
+ struct smtp *smtpb; /* SMTP description block */
+ char **reply_text; /* text of response from remote */
+{
+ register FILE *f = smtpb->out;
+
(void) fflush(f);
if (ferror(f)) {
*reply_text = "499 write error, remote probably down";
DEBUG1(DBG_DRIVER_MID, "SMTP-reply: %s\n", *reply_text);
return REPLY_TIMEOUT;
}
-
- /* wait for the response to come back */
- if (smtpb->in) {
- reply = wait_read_response(smtpb, timeout, reply_text);
- DEBUG1(DBG_DRIVER_MID, "SMTP-reply: %s\n", *reply_text);
- return reply;
- }
return REPLY_OK;
}
@@ -445,6 +787,27 @@
*/
static int
wait_read_response(smtpb, timeout, reply_text)
+ struct smtp *smtpb; /* SMTP description block */
+ unsigned timeout; /* read timeout */
+ char **reply_text; /* return text of response here */
+{
+ int result;
+
+ /* If we're in batch mode, always return success. */
+ if (! smtpb->in) {
+ return REPLY_OK;
+ }
+ do
+ {
+ result = read_response_internal (smtpb, timeout, reply_text);
+ DEBUG1(DBG_DRIVER_MID, "SMTP-reply: %s\n", *reply_text);
+ }
+ while (REPLY_GROUP (result) == POSITIVE_DEBUG);
+ return result;
+}
+
+static int
+read_response_internal(smtpb, timeout, reply_text)
struct smtp *smtpb; /* SMTP description block */
unsigned timeout; /* read timeout */
char **reply_text; /* return text of response here */
--- src/transports/smtplib.h 1994/11/14 09:46:49 1.1
+++ src/transports/smtplib.h 1994/12/13 10:59:54
@@ -1,4 +1,4 @@
-/* @(#) $Id: smtplib.h,v 1.1 1994/11/14 09:46:49 logiciel Exp $ */
+/* @(#) $Id: smtplib.h,v 1.4 1994/12/13 10:59:53 logiciel Exp $ */
/*
* Copyright (C) 1987, 1988 by Ronald S. Karr and Landon Curt Noll
@@ -17,6 +17,20 @@
#define SMTP_SUCCEED 0
#define SMTP_FAIL (-1)
#define SMTP_AGAIN (-2)
+#define SMTP_EHLO_FAIL (-3)
+
+typedef enum
+{
+ ESMTP_none = 0x0000,
+ ESMTP_basic = 0x0001,
+ ESMTP_8bitmime = 0x0002,
+ ESMTP_size = 0x0004,
+ ESMTP_pipelining = 0x0008,
+ ESMTP_verbose = 0x0010,
+ ESMTP_one = 0x0020,
+ ESMTP_queue = 0x0040
+}
+SMTPExtension, SMTPExtensionFlags;
/*
* the following structure is passed around between SMTP functions and
@@ -32,6 +46,8 @@
unsigned long_timeout; /* normal SMTP read timeout period */
char *nl; /* line terminator string */
struct transport *tp; /* associated transport */
+ SMTPExtensionFlags smtp_flags; /* SMTP extensions supported by remote */
+ unsigned long max_size; /* message size limit of remote */
};
/* functions defined in smtplib.c */
--- src/transports/tcpsmtp.c 1994/11/14 09:47:25 1.1
+++ src/transports/tcpsmtp.c 1994/12/13 11:29:15
@@ -288,6 +288,7 @@
char *error_text;
int success;
struct addr *ap;
+ int try_ehlo;
priv = (struct tcpsmtp_private *)tp->private;
@@ -310,41 +311,53 @@
/* reach out and touch someone */
- s = tcpsmtp_connect(hostname, ipaddr, family, service, &error_text);
- if (s >= 0) {
- s2 = dup(s);
- if (s2 < 0) {
- (void) close(s);
- s = -1;
- }
- }
- if (s < 0) {
- *ep = connect_failure(tp, error_text);
- return SMTP_AGAIN;
- }
-
- smtpbuf.in = fdopen(s, "r");
- smtpbuf.out = fdopen(s2, "w");
- smtpbuf.short_timeout = priv->short_timeout;
- smtpbuf.long_timeout = priv->long_timeout;
- smtpbuf.nl = "\r\n";
- tp->flags |= PUT_CRLF;
- smtpbuf.tp = tp;
-
- DEBUG(DBG_DRIVER_LO, "connected\n");
-
- switch (smtp_startup(&smtpbuf, ep)) {
-
- case SMTP_FAIL:
- insert_addr_list(addr, fail, *ep);
- (void) fclose(smtpbuf.in);
- (void) fclose(smtpbuf.out);
- return SMTP_FAIL;
-
- case SMTP_AGAIN:
- (void) fclose(smtpbuf.in);
- (void) fclose(smtpbuf.out);
- return SMTP_AGAIN;
+ for (try_ehlo = 1, success = 0; !success && try_ehlo >= 0; --try_ehlo) {
+ s = tcpsmtp_connect(hostname, ipaddr, family, service, &error_text);
+ if (s >= 0) {
+ s2 = dup(s);
+ if (s2 < 0) {
+ (void) close(s);
+ s = -1;
+ }
+ }
+ if (s < 0) {
+ *ep = connect_failure(tp, error_text);
+ return SMTP_AGAIN;
+ }
+
+ smtpbuf.in = fdopen(s, "r");
+ smtpbuf.out = fdopen(s2, "w");
+ smtpbuf.short_timeout = priv->short_timeout;
+ smtpbuf.long_timeout = priv->long_timeout;
+ smtpbuf.nl = "\r\n";
+ tp->flags |= PUT_CRLF;
+ smtpbuf.tp = tp;
+ smtpbuf.smtp_flags = ESMTP_none;
+ smtpbuf.max_size = 0;
+
+ DEBUG(DBG_DRIVER_LO, "connected\n");
+
+ switch (smtp_startup(&smtpbuf, ep, try_ehlo)) {
+
+ case SMTP_FAIL:
+ insert_addr_list(addr, fail, *ep);
+ (void) fclose(smtpbuf.in);
+ (void) fclose(smtpbuf.out);
+ return SMTP_FAIL;
+
+ case SMTP_AGAIN:
+ (void) fclose(smtpbuf.in);
+ (void) fclose(smtpbuf.out);
+ return SMTP_AGAIN;
+
+ case SMTP_EHLO_FAIL:
+ (void) fclose(smtpbuf.in);
+ (void) fclose(smtpbuf.out);
+ break;
+
+ default:
+ success = 1;
+ }
}
if (dont_deliver) {
--- /dev/null Tue Dec 13 15:30:59 1994
+++ src/geniobpeek.sh Mon Nov 14 10:48:56 1994
@@ -0,0 +1,81 @@
+#!/bin/sh
+#
+# This script tries to generate a file "iobpeek.h" which defines one
+# macro, IOB_MAYBE_EMPTY_P. The macro can be called with one
+# argument, a stdio input stream, and must return a non-zero value if
+# the stream's buffer is empty. If there are characters in the input
+# buffer, it *should* return zero.
+
+# work around brain-damaged Ultrix shell
+if test -z "$foo" -a -r /bin/sh5
+then
+ export foo; foo=bar; exec /bin/sh5 $0 "$1" "$2"
+fi
+
+CC=$1; shift
+CFLAGS=$1; shift
+ECHO=echo
+#ECHO=:
+
+rm -f ,testfile
+
+try ()
+{
+ echo foo bar > ,testfile
+ empty_test="$1"
+ $ECHO "Trying $empty_test"
+ rm -f ,iobtest.c
+ cat > ,iobtest.c <<EOM
+#include <stdio.h>
+
+#define IOB_MAYBE_EMPTY_P(stream) $empty_test
+
+main (argc, argv) int argc; char **argv;
+{
+ FILE * in = fopen(",testfile", "r");
+ int c;
+ if (! in) { fprintf (stderr, "Couldn't open test file\n"); exit (1); }
+ if (! IOB_MAYBE_EMPTY_P (in)) {
+ fprintf (stderr, "Buffer not empty after fopen()\n"); exit (1);
+ }
+ c = getc(in);
+ if (c != 'f') { fprintf (stderr, "Read error\n"); exit (1); }
+ if (IOB_MAYBE_EMPTY_P (in)) {
+ fprintf (stderr, "Buffer empty after reading one char\n"); exit (1);
+ }
+ return 0;
+}
+EOM
+
+ $CC $CFLAGS -o ,iobtest ,iobtest.c >/dev/null 2>&1 || \
+ ( $ECHO " compilation failed"; return 1 )
+ if ./,iobtest
+ then
+ cat > iobpeek.h <<EOM
+/* iobpeek.h - DO NOT EDIT!
+ Automatically generated by geniobpeek.sh */
+#define IOB_MAYBE_EMPTY_P(stream) $empty_test
+EOM
+ status=0
+ else
+ status=1
+ fi
+ rm ,iobtest ,iobtest.c ,testfile
+ return $status
+}
+
+# IRIX, SunOS 4, Solaris 2
+try '((stream)->_cnt == 0)' && exit 0
+# Older systems may have this
+try '((stream)->cnt == 0)' && exit 0
+# Linux
+try '((stream)->_IO_read_ptr >= (stream)->_IO_read_end)' && exit 0
+# GNU libc 1.08.1
+try '((stream)->bufp >= (stream)->__get_limit)' && exit 0
+
+cat 1>&2 <<EOM
+*** Error: could not find out how to check for an empty stdio stream
+*** buffer on this system. Please notify Simon Leinen
+*** <simon@lia.di.epfl.ch> of this problem.
+EOM
+exit 1